using System;
using System.IO;
using eee.Sheffield.PZ.Math;
using eee.Sheffield.PZ.Imaging.ImageFilter;

namespace eee.Sheffield.PZ.Imaging
{
    /// <summary>
    /// Sun scanline 
    /// </summary>
    public class SunScanline
    {
        #region Fields
		private int _widthOfScanline = 0;		/// width of scanline
        private int _length = 0;				// length of data, e.g. fg

        // scanline data 
        private PZMath_vector _g = new PZMath_vector();				// intensities, sample values
        private PZPoint[] _gPoint = null;			// sample points coordinate
        private PZMath_vector _output = new PZMath_vector();         // output of convoluting with the matched filter
        
        // estimates
        private int _centre = 0;     // centre pos
        private double _initr = 0.0; // initial radius
        private double _r = 0.0;     // radius
        private double _signalLevel = 0.0;       // signal level
        private double _backgroundLevel = 0.0;   // background level      
        private PZMath_vector _edgePositions = new PZMath_vector();
        private string _exitMsg = " ";

        private double _gamaThreshold = 0.0;  // a threshold of percent dynamic range of signal, 
        private double _gama = 0.0;          // percent dynamic range of signal

        private SunMatchedFilter matchedFilter = new SunMatchedFilter();
	    #endregion

        #region Property
        public int WidthOfScanline { get { return _widthOfScanline; } set { _widthOfScanline = value; } }
        public int Length { get { return _length; } set { _length = value; } }
        public PZMath_vector ScanlineProfile { get { return _g; } set { _g = value; } }
        public PZPoint[] ScanlinePoint { get { return _gPoint; } }
        public PZMath_vector MatchedFilterOutput { get { return _output; } }

        public int Centre { get { return _centre; } set { _centre = value; } }
        public double InitRadius { get { return _initr; } set { _initr = value; } }
        public double Radius { get { return _r; } set { _r = value; } }
        public double SignalLevel { get { return _signalLevel; } set { _signalLevel = value; } }
        public double BackgroundLevel { get { return _backgroundLevel; } set { _backgroundLevel = value; } }
        public PZMath_vector EdgePositions { get { return _edgePositions; } set { _edgePositions.MemCopyFrom(value); } }
        public string ExitMsg { get { return _exitMsg; } set { _exitMsg = String.Copy(value); } }
        public double GamaTherehold { get { return _gamaThreshold; } set { _gamaThreshold = value; } }
        public double Gama { get { return _gama; } set { _gama = value; } }
        #endregion

        // mem copy
        public void MemCopyFrom(SunScanline sl)
        {
            _widthOfScanline = sl._widthOfScanline;
            _length = sl._length;
            _initr = sl._initr;
            _r = sl._r;
            _centre = sl._centre;
            _signalLevel = sl._signalLevel;
            _backgroundLevel = sl._backgroundLevel;
            _edgePositions.MemCopyFrom(sl.EdgePositions);

            _exitMsg = String.Copy(sl._exitMsg);

            _gamaThreshold = sl._gamaThreshold;
            _gama = sl._gama;

            _g = new PZMath_vector(sl._g);

            _gPoint = new PZPoint[_length];
            Array.Copy(sl._gPoint, _gPoint, _length);

            _output.MemCopyFrom(sl._output);           
        } // MemCopyFrom()

        // check percent dynamic range of signal
        public bool IsFit()
        {            
            bool isfit = true;
            if (_gama < _gamaThreshold)
            {
                isfit = false;
                _exitMsg += "gama < threshold";
            }                       
            return isfit;
        } // IsFit()

        #region constructors

        /// <summary>
        /// empty structor
        /// </summary>
        public SunScanline() { }

        public SunScanline(double initr, double gamaThreshold)
        {
            _initr = initr;
            _widthOfScanline = (int)System.Math.Round(initr * 2.0 + 4.0);
            //_widthOfScanline = (int)System.Math.Round(4.0 * initr);
            _length = _widthOfScanline;
            _g = new PZMath_vector(_length);
            _gamaThreshold = gamaThreshold;
            _gPoint = new PZPoint[_length];
            for (int i = 0; i < _length; i++)
                _gPoint[i] = new PZPoint();
        } // SunScanline()

        /// <summary>
        /// generate from 2 points
        /// </summary>
        /// <param name="start"></param>
        /// <param name="end"></param>
        public SunScanline(PZPoint start, PZPoint end, double gamaThreshold)
        {
            _gamaThreshold = gamaThreshold;
            double width = start.Distance(end);
            _widthOfScanline = (int)width;
            _initr = (width - 4.0) / 2.0;
            _length = _widthOfScanline;
            _g = new PZMath_vector(_length);
            _gPoint = new PZPoint[_length];
            for (int i = 0; i < _length; i++)
                _gPoint[i] = new PZPoint();
        } // SunScanline()
        #endregion

        #region matched filter methods        
        public void Analysis()
        {
            // matched filter
            _output = matchedFilter.RectangularMatchedFilter(_g, (int)_initr);
            // centre pos
            _centre = _g.MaxIndex();
            // signal level
            _signalLevel = matchedFilter.SignalLevel(_g, _centre, _initr);
            // background level
            _backgroundLevel = matchedFilter.BackgroundLevel(_g, _centre, _initr);
            // roll-off position
            double rollOffPoint = matchedFilter.RollOffPoint(_signalLevel, _backgroundLevel);
            // edge positions
            _edgePositions = matchedFilter.EdgePoints(_g, rollOffPoint, ProfileShape.Peak);
            // radius
            _r = (_edgePositions[1] - _edgePositions[0] + 1.0) / 2.0;
            // update centre position
            _centre = (int)(_edgePositions[0] + _r);
            // gama
            if (_backgroundLevel == 0.0)
                _gama = 0.0;
            else
            {
                _gama = (_signalLevel - _backgroundLevel) / _backgroundLevel;
            }
        } // Analysis()        
        #endregion

        #region file IO methods
        public void WriteFile(string filename)
        {
            FileStream file = new FileStream(filename, FileMode.Create, FileAccess.Write);
            StreamWriter sw = new StreamWriter(file);
            for (int i = 0; i < _length; i++)
                sw.WriteLine(_g[i].ToString());
            sw.Close();
            file.Close();
        } // WriteFile()
        #endregion
    } // SunScanline
}
